import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import assignmentimplementation.BeginGreaterThanEndException_Exception;
import assignmentimplementation.FileNotFoundException_Exception;
import assignmentimplementation.IOException_Exception;
import assignmentimplementation.KeyAlreadyPresentException_Exception;
import assignmentimplementation.KeyImpl;
import assignmentimplementation.KeyNotFoundException_Exception;
import assignmentimplementation.KeyValueBaseImpl;
import assignmentimplementation.KeyValueBaseImplService;
import assignmentimplementation.LongerThanFivePred;
import assignmentimplementation.ServiceAlreadyInitializedException_Exception;
import assignmentimplementation.ServiceInitializingException_Exception;
import assignmentimplementation.ServiceNotInitializedException_Exception;
import assignmentimplementation.ValueImpl;
import assignmentimplementation.ValueListImpl;


public class CommandLine {

	private boolean running;
	private Pattern funcPattern;
	private KeyValueBaseImpl port;
	

	/**
	 * @param port 
	 * @param running 
	 * @param args
	 */
	public CommandLine(){
		System.out.println("Client Started.");
		Scanner in = new Scanner( System.in );
		running = true;
		KeyValueBaseImplService serv = new KeyValueBaseImplService(); 
		this.port = serv.getKeyValueBaseImplPort();
		funcPattern = Pattern.compile("([a-zA-Z]+)[ ]*\\(([ ,:._//-/\\,a-zA-Z0-9]+)\\)");
		loop(in);
	}


	private void loop(Scanner in){
		String line;
		while(running){
			line = in.nextLine();
			command(line);
		}
	}


	private void printHelp() {
		System.out.println("List of commands: init(filepath), read(int), insert(int values),");
		System.out.println("update(int values), read(int), delete(int), scan(int int int)");
		System.out.println("bulkput(int values , int values...), atomicscan(int, int, int");
		System.out.println("Keys are expressed as integers, values can be numbers or characters");
		System.out.println("elementas are separated by whitespace. For scan, the third integer expresses");
		System.out.println("the kind of predicate you want:");
		System.out.println("1: checks if all values for a key have more than 5 characters");
		System.out.println("for bulkput, key-value pairs are separated by a comma.");
	}

	private void command(String line) {
		line = line.trim();
		
		
		if(line.isEmpty());
		
		else if(funcPattern.matcher(line).matches()){
			Matcher m = funcPattern.matcher(line);
			m.matches();
			String command = m.group(1);
			String parameters = m.group(2);
			executeCommand(command, parameters);
		}
		
		
		else if (line.equalsIgnoreCase("exit")){
			running = false;		
		}
		
		else if (line.equalsIgnoreCase("help")){		
			printHelp();
		}
		
		else
			System.out.println("Command format: name ( parameters ), "+
					"for more info type help");
			
	}


	private void executeCommand(String command, String parameters) {
		command = command.trim();
		parameters = parameters.trim();
		
		if(command.equalsIgnoreCase("init")){
			try {
				port.init(parameters);
				System.out.println("Server Initialized");
			} catch (FileNotFoundException_Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ServiceAlreadyInitializedException_Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ServiceInitializingException_Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		else if (command.equalsIgnoreCase("read")){

			if(parameters.matches("[0-9]+")){
				KeyImpl k = new KeyImpl();
				k.setKeydata(Integer.parseInt(parameters));
				
				try {
					ValueListImpl l = port.read(k);
					Iterator<ValueImpl> iter = l.getValueList().iterator();
					while(iter.hasNext()){
						System.out.println(iter.next().getValue());
					}
				} catch (IOException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (KeyNotFoundException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ServiceNotInitializedException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
			else{
				System.out.println("Erroneous parameter: " + parameters);
			}
		}
			
		else if (command.equalsIgnoreCase("insert")){
			
			Pattern pattern = Pattern.compile("([0-9]+)[ ]*([ 0-9a-zA-Z]+)");
			Matcher m = pattern.matcher(parameters);
			if(m.matches()){
				KeyImpl k = new KeyImpl();
				k.setKeydata(Integer.parseInt(m.group(1)));
				
				StringTokenizer token = new StringTokenizer(m.group(2));
				String str;
				ValueListImpl l = new ValueListImpl();
				
				while(token.hasMoreTokens()){
					str = token.nextToken();
					ValueImpl v = new ValueImpl();
					v.setValue(str);
					l.getValueList().add(v);
				}
				
				try {
					port.insert(k, l);
					System.out.println("Inserted values " + m.group(2) + " into key " + m.group(1));
				} catch (IOException_Exception
						| KeyAlreadyPresentException_Exception
						| ServiceNotInitializedException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				
				
			}
			
			else{
				System.out.println("Erroneous parameters: " + parameters);
			}
		}
		
		else if (command.equalsIgnoreCase("update")){
			
			Pattern pattern = Pattern.compile("([0-9]+)[ ]*([ 0-9a-zA-Z]+)");
			Matcher m = pattern.matcher(parameters);
			if(m.matches()){
				KeyImpl k = new KeyImpl();
				k.setKeydata(Integer.parseInt(m.group(1)));
				
				StringTokenizer token = new StringTokenizer(m.group(2));
				String str;
				ValueListImpl l = new ValueListImpl();
				
				while(token.hasMoreTokens()){
					str = token.nextToken();
					ValueImpl v = new ValueImpl();
					v.setValue(str);
					l.getValueList().add(v);
				}
				
				try {
					port.update(k, l);
					System.out.println("Updated key: " + m.group(1) + " with new values: " + m.group(2));
				} catch (IOException_Exception
						| ServiceNotInitializedException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (KeyNotFoundException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				
				
			}
			
			else{
				System.out.println("Erroneous parameters: " + parameters);
			}
		}
		else if (command.equalsIgnoreCase("delete")){
			if(parameters.matches("[0-9]+")){
				KeyImpl k = new KeyImpl();
				k.setKeydata(Integer.parseInt(parameters));
				
				try {
					port.delete(k);
					System.out.println("Key " + k.getKeydata() + " deleted");
				} catch (KeyNotFoundException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ServiceNotInitializedException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
			else{
				System.out.println("Erroneous parameter: " + parameters);
			}
		}
		else if (command.equalsIgnoreCase("scan")){

			Pattern pattern = Pattern.compile("([0-9]+)[ ]*([0-9]+)[ ]*([0-9]+)");
			Matcher m = pattern.matcher(parameters);
			if(m.matches()){
				
				StringTokenizer token = new StringTokenizer(parameters);
				String str;
				str = token.nextToken();
				
				KeyImpl k1 = new KeyImpl();
				k1.setKeydata(Integer.parseInt(str));
				str = token.nextToken();
				KeyImpl k2 = new KeyImpl();
				k2.setKeydata(Integer.parseInt(str));
				str = token.nextToken();
				boolean flag = false;
				try {
					List<ValueListImpl> l = null;
					if(str.equalsIgnoreCase("1")){
						l =  port.scan(k1, k2, new LongerThanFivePred()); // -this nees changing
						flag = true;
					}
					
					if(flag){
						Iterator<ValueListImpl> iter = l.iterator();
						if(!iter.hasNext()){
							System.out.println("No keys where found to satisfy the predicate");
						}
						while(iter.hasNext()){
							ValueListImpl vli = iter.next();
							Iterator<ValueImpl> itervli = vli.getValueList().iterator();
							while(itervli.hasNext())
								System.out.println(itervli.next().getValue());
						}
					}
					else
						System.out.println("Unknown predicate number");

				} catch (IOException_Exception
						| ServiceNotInitializedException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (BeginGreaterThanEndException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
			
			else{
				System.out.println("Erroneous parameters: " + parameters);
			}
		}
		else if (command.equalsIgnoreCase("bulkput")){
			Pattern pattern = Pattern.compile("([0-9]+)[ ]*([ 0-9a-zA-Z]+)([ ]*(,)[ ]*([0-9]+)[ ]*([ 0-9a-zA-Z]+))*");
			Matcher m = pattern.matcher(parameters);
			if(m.matches()){
				
				List<assignmentimplementation.Pair> mappings = new ArrayList();
				
				StringTokenizer ct = new StringTokenizer(parameters, ",");
				
				while(ct.hasMoreTokens()){
					StringTokenizer token = new StringTokenizer(ct.nextToken());
					KeyImpl k = new KeyImpl();
					k.setKeydata(Integer.parseInt(token.nextToken()));
					
					String str;
					ValueListImpl vl = new ValueListImpl(); 
					List<ValueImpl> l = vl.getValueList();
					ValueImpl val;
					
					while(token.hasMoreTokens()){
						str = token.nextToken();
						val = new ValueImpl();
						val.setValue(str);
						l.add(val);
					}
					
					assignmentimplementation.Pair p = new assignmentimplementation.Pair();
					p.setK(k);
					p.setV(vl);
					mappings.add(p);
				}
				
				try {
					port.bulkPut(mappings);
					System.out.println("Bulkput complete");
				} catch (IOException_Exception
						| ServiceNotInitializedException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				

			}
			else{
				System.out.println("Erroneous parameters: " + parameters);
			}
		}
		else if (command.equalsIgnoreCase("atomicscan")){
			Pattern pattern = Pattern.compile("([0-9]+)[ ]*([0-9]+)[ ]*([0-9]+)");
			Matcher m = pattern.matcher(parameters);
			if(m.matches()){
				
				StringTokenizer token = new StringTokenizer(parameters);
				String str;
				str = token.nextToken();
				
				KeyImpl k1 = new KeyImpl();
				k1.setKeydata(Integer.parseInt(str));
				str = token.nextToken();
				KeyImpl k2 = new KeyImpl();
				k2.setKeydata(Integer.parseInt(str));
				str = token.nextToken();
				boolean flag = false;
				try {
					List<ValueListImpl> l = null;
					if(str.equalsIgnoreCase("1")){
						l =  port.scan(k1, k2, new LongerThanFivePred()); // -this nees changing
						flag = true;
					}
					
					if(flag){
						Iterator<ValueListImpl> iter = l.iterator();
						while(iter.hasNext()){
							ValueListImpl vli = iter.next();
							Iterator<ValueImpl> itervli = vli.getValueList().iterator();
							while(itervli.hasNext())
								System.out.println(itervli.next().getValue());
						}
					}
					else
						System.out.println("Unknown predicate number");

				} catch (IOException_Exception
						| ServiceNotInitializedException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (BeginGreaterThanEndException_Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
			
			else{
				System.out.println("Erroneous parameters: " + parameters);
			}
		}
		else {
			System.out.println("Unknown Command: " + command);
		}
	}
}
